#ifndef AVALON_TUPLEMESSAGE_HPP
#define AVALON_TUPLEMESSAGE_HPP
#include <boost/call_traits.hpp>
#include <boost/tuple/tuple.hpp>
#include "Avalon/Serialization/DataShuttle.hpp"
#include "Avalon/Serialization/ShuttleTuple.hpp"
#include "Avalon/Services/Message.hpp"

namespace Avalon {
namespace Services {
  template<typename T, typename A0 = Detail::NullType,
    typename A1 = Detail::NullType, typename A2 = Detail::NullType,
    typename A3 = Detail::NullType, typename A4 = Detail::NullType,
    typename A5 = Detail::NullType, typename A6 = Detail::NullType,
    typename A7 = Detail::NullType, typename A8 = Detail::NullType,
    typename A9 = Detail::NullType, typename A10 = Detail::NullType>
  class TupleMessage {};

  template<typename T>
  class TupleMessage<T> : public Message {
    public:
      typedef T Name;
      typedef boost::signals2::signal<void (IO::Channel*)> Signal;

      static int InitializeMessage(const char* name) {
        Serialization::DataShuttle::RegisterType<TupleMessage>(name, 0);
        return 0;
      }

      TupleMessage() {}

    protected:
      virtual void EmitSignal(boost::signals2::signal_base* signal,
          ServiceNode* serviceNode, IO::Channel* channel) {
        (*static_cast<Signal*>(signal))(channel);
      }

      virtual void Shuttle(Serialization::DataShuttle* shuttle,
          unsigned int version) {
        shuttle->Shuttle("elements", m_tuple);
      }

    private:
      friend class Serialization::DataShuttle;
      boost::tuple<> m_tuple;
  };

  #ifndef AVALON_TUPLE_MESSAGE_PARAMETERS
    #define AVALON_TUPLE_MESSAGE_PARAMETERS 10
  #endif

  #define AVALON_TUPLE_MESSAGE_PASS_PARAMETER(z, n, a)                         \
    BOOST_PP_COMMA_IF(n) typename boost::call_traits<a##n>::param_type

  #define AVALON_TUPLE_MESSAGE_DEFINE_PARAMETER(z, n, q)                       \
    BOOST_PP_COMMA_IF(n) typename boost::call_traits<q##n>::param_type a##n

  #define AVALON_TUPLE_MESSAGE_GET_TUPLE_COMMA_COPY(z, n, a)                   \
    BOOST_PP_COMMA_IF(n) boost::get<n>(a)

  #define AVALON_TUPLE_MESSAGE_PARAMETERS_COMMA_COPY(z, n, a)                  \
    BOOST_PP_COMMA_IF(n) a##n

  #define BOOST_PP_LOCAL_MACRO(n)                                              \
  template<typename T, BOOST_PP_ENUM_PARAMS(n, typename A)>                    \
  class TupleMessage<T, BOOST_PP_ENUM_PARAMS(n, A)> : public Message {         \
    public:                                                                    \
      typedef T Name;                                                          \
      typedef boost::signals2::signal<void (IO::Channel*,                      \
        BOOST_PP_REPEAT(n, AVALON_TUPLE_MESSAGE_PASS_PARAMETER, A))> Signal;   \
                                                                               \
      static int InitializeMessage(const char* name) {                         \
        Serialization::DataShuttle::RegisterType<TupleMessage>(name, 0);       \
        return 0;                                                              \
      }                                                                        \
                                                                               \
      TupleMessage() {}                                                        \
                                                                               \
      TupleMessage(BOOST_PP_REPEAT(n,                                          \
          AVALON_TUPLE_MESSAGE_DEFINE_PARAMETER, A))                           \
          : m_tuple(BOOST_PP_REPEAT(n,                                         \
              AVALON_TUPLE_MESSAGE_PARAMETERS_COMMA_COPY, a)) {}               \
                                                                               \
    protected:                                                                 \
      virtual void EmitSignal(boost::signals2::signal_base* signal,            \
          ServiceNode* serviceNode, IO::Channel* channel) {                    \
        (*static_cast<Signal*>(signal))(channel, BOOST_PP_REPEAT(n,            \
          AVALON_TUPLE_MESSAGE_GET_TUPLE_COMMA_COPY, m_tuple));                \
      }                                                                        \
                                                                               \
      virtual void Shuttle(Serialization::DataShuttle* shuttle,                \
          unsigned int version) {                                              \
        shuttle->Shuttle("elements", m_tuple);                                 \
      }                                                                        \
                                                                               \
    private:                                                                   \
      friend class Serialization::DataShuttle;                                 \
      boost::tuple<BOOST_PP_ENUM_PARAMS(n, A)> m_tuple;                        \
  };

  #define BOOST_PP_LOCAL_LIMITS                                                \
    (1, AVALON_TUPLE_MESSAGE_PARAMETERS)
  #include BOOST_PP_LOCAL_ITERATE()

  #define AVALON_DECLARE_MESSAGE(Type, Name, ...)                              \
    struct Type##Token {};                                                     \
    typedef ::Avalon::Services::TupleMessage<Type##Token, ##__VA_ARGS__> Type; \
    namespace {                                                                \
      int Type##AVALON_UNIQUE_NAME = Type::InitializeMessage(Name);            \
    }
}
}

#endif // AVALON_TUPLEMESSAGE_HPP
